﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;
using System.Net;
using XUCore.Extensions;
using XUCore.NetCore.DynamicWebApi.Helper;
using Newtonsoft.Json;

namespace XUCore.NetCore.RestFul
{
    /// <summary>
    /// Api返回过滤器
    /// </summary>
    public class RestFulFormatterFilter : ActionFilterAttribute, IActionFilter
    {
        private string ElapsedField = "ElapsedTime";
        private bool Open = true;

        public RestFulFormatterFilter(bool Open = true, string ElapsedField = "ElapsedTime")
        {
            this.Open = Open;
            this.ElapsedField = ElapsedField;
        }

        private Stopwatch stopwatch = new Stopwatch();

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (Open)
            {
                if (stopwatch == null)
                    stopwatch = new Stopwatch();
                stopwatch.Reset();
                stopwatch.Restart();
            }
        }

        public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
        {
            if (Open)
            {
                if (stopwatch == null)
                    stopwatch = new Stopwatch();
                stopwatch.Stop();

                //前端指定是否输出restful格式 非true 则直接返回内容
                var noRestFul = actionExecutedContext.HttpContext.Request.Headers["limit-no-restful"].SafeString().ToLower();
                if (noRestFul.NotEmpty() && noRestFul.Equals("true")) return;

                var descriptor = (ControllerActionDescriptor)actionExecutedContext.ActionDescriptor;

                var controllerAttr = descriptor.ControllerTypeInfo.GetSingleAttributeOrNull<NoRestFulFormatterAttribute>();
                if (controllerAttr != null) return;

                //转换结果的类型
                var resultType = actionExecutedContext.HttpContext.Request.Headers["limit-data-type"].SafeString().ToLower();
                //保持前端配置的一致
                var options = actionExecutedContext.HttpContext.RequestServices.GetRequiredService<FormatterOptions>();
                Utils.FormatterJsonOptions(actionExecutedContext.HttpContext, options);

                var reType = actionExecutedContext.Result?.GetType();

                if (reType == typeof(Result))
                {
                    var res = (Result)actionExecutedContext.Result;
                    var objResult = default(ObjectResult);
                    switch (resultType)
                    {
                        case "string":
                            objResult = new ObjectResult(new Result<string>(res.Code, res.SubCode, res.Message, JsonConvert.SerializeObject(res?.Data, options.JsonSerializerSettings))
                            {
                                ElapsedTime = stopwatch.ElapsedMilliseconds,
                                OperationTime = res.OperationTime
                            });
                            break;
                        default:
                            objResult = new ObjectResult(new Result<object>(res.Code, res.SubCode, res.Message, res?.Data)
                            {
                                ElapsedTime = stopwatch.ElapsedMilliseconds,
                                OperationTime = res.OperationTime
                            });
                            break;
                    }
                    objResult.StatusCode = (int)HttpStatusCode.OK;
                    actionExecutedContext.Result = objResult;
                }
                else if (reType == typeof(ObjectResult))
                {
                    var res = (ObjectResult)actionExecutedContext.Result;
                    if (res != null && res.Value != null && res.Value.GetType().Name == typeof(Result<>).Name)
                    {
                        switch (resultType)
                        {
                            case "string":
                                {
                                    var codeValue = (StateCode)res.Value?.GetType().GetProperty("Code").GetValue(res.Value);
                                    var subCodeValue = (string)res.Value?.GetType().GetProperty("SubCode").GetValue(res.Value);
                                    var messageValue = (string)res.Value?.GetType().GetProperty("Message").GetValue(res.Value);
                                    var dataValue = res.Value?.GetType().GetProperty("Data").GetValue(res.Value);

                                    res.DeclaredType = typeof(Result<string>);
                                    res.Value = new Result<string>(codeValue, subCodeValue, messageValue, JsonConvert.SerializeObject(dataValue, options.JsonSerializerSettings))
                                    {
                                        ElapsedTime = stopwatch.ElapsedMilliseconds
                                    };
                                }
                                break;
                            default:
                                {
                                    res.Value?.GetType().GetProperty(ElapsedField).SetValue(res.Value, stopwatch.ElapsedMilliseconds);
                                }
                                break;
                        }
                        res.StatusCode = (int)HttpStatusCode.OK;
                        actionExecutedContext.Result = res;
                    }
                    else
                    {
                        var actionAttr = descriptor.MethodInfo.GetSingleAttributeOrNull<NoRestFulFormatterAttribute>();
                        if (actionAttr != null) return;

                        var objResult = default(ObjectResult);
                        switch (resultType)
                        {
                            case "string":
                                objResult = new ObjectResult(new Result<string>(StateCode.Success, "SUCCESS", "操作成功", JsonConvert.SerializeObject(res?.Value, options.JsonSerializerSettings))
                                {
                                    ElapsedTime = stopwatch.ElapsedMilliseconds
                                });
                                break;
                            default:
                                objResult = new ObjectResult(new Result<object>(StateCode.Success, "SUCCESS", "操作成功", res?.Value)
                                {
                                    ElapsedTime = stopwatch.ElapsedMilliseconds
                                });
                                break;
                        }
                        objResult.StatusCode = (int)HttpStatusCode.OK;
                        actionExecutedContext.Result = objResult;
                    }
                }
            }
        }
    }
}